﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace NuMvvm
{
    public abstract class ObservableObject : INotifyPropertyChanged
    {
        #region Change notification

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException(propertyName);
            }

            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> propertyExpression)
        {
            if (propertyExpression == null)
            {
                throw new ArgumentNullException("propertyExpression");
            }
            string propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
            OnPropertyChanged(propertyName);
        }

        protected bool SetProperty<TProperty>(Expression<Func<TProperty>> propertyExpression, ref TProperty target, TProperty value)
        {
            if (propertyExpression == null)
                throw new ArgumentNullException("propertyExpression");

            var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
            return SetProperty(ref target, value, propertyName);
        }

        protected bool SetProperty<TProperty>(ref TProperty target, TProperty value, [CallerMemberName] string propertyName = null)
        {
            if (propertyName == null)
                throw new ArgumentNullException("propertyName");

            if (Equals(target, value)) return false;

            target = value;
            OnPropertyChanged(propertyName);

            return true;
        }


        #endregion

        #region Implements INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Helpers

        protected object GetPropertyValue(string propertyName)
        {
            return GetType().GetTypeInfo()
                .GetDeclaredProperty(propertyName)
                .GetValue(this);
        }

        #endregion
    }
}
